<!DOCTYPE html>
<html>
<head hexo-theme='https://github.com/volantis-x/hexo-theme-volantis/tree/4.0.0-rc.4'>
  <meta charset="utf-8">
  <!-- SEO相关 -->
  
    
  
  <!-- 渲染优化 -->
  <meta name="renderer" content="webkit">
  <meta name="force-rendering" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="HandheldFriendly" content="True" >
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta http-equiv='x-dns-prefetch-control' content='on' />

  <!-- 页面元数据 -->
  
  <title>基于小波变换的数字水印检测 - CC康纳百川</title>
  
    <meta name="keywords" content="study,watermark,matlab">
  

  

  <!-- feed -->
  

  <!-- import meta -->
  

  <!-- link -->
  <link rel='dns-prefetch' href='https://cdn.jsdelivr.net'>
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.14/css/all.min.css">

  
  

  

  

  

  

  <!-- import link -->
  

  
  
    
<link rel="stylesheet" href="/volantis/css/style.css">

  
  
  
  <!-- 脚本懒加载函数 -->
  <script>
  function loadScript(src, cb) {
    var HEAD = document.getElementsByTagName('head')[0] || document.documentElement;
    var script = document.createElement('script');
    script.setAttribute('type','text/javascript');
    if (cb) script.onload = cb;
    script.setAttribute('src', src);
    HEAD.appendChild(script);
  }
  //https://github.com/filamentgroup/loadCSS
  !function(c){"use strict";var e=function(e,t,n,r){var o,i=c.document,a=i.createElement("link");if(t)o=t;else{var d=(i.body||i.getElementsByTagName("head")[0]).childNodes;o=d[d.length-1]}var f=i.styleSheets;if(r)for(var l in r)r.hasOwnProperty(l)&&a.setAttribute(l,r[l]);a.rel="stylesheet",a.href=e,a.media="only x",function e(t){if(i.body)return t();setTimeout(function(){e(t)})}(function(){o.parentNode.insertBefore(a,t?o:o.nextSibling)});var s=function(e){for(var t=a.href,n=f.length;n--;)if(f[n].href===t)return e();setTimeout(function(){s(e)})};function u(){a.addEventListener&&a.removeEventListener("load",u),a.media=n||"all"}return a.addEventListener&&a.addEventListener("load",u),(a.onloadcssdefined=s)(u),a};"undefined"!=typeof exports?exports.loadCSS=e:c.loadCSS=e}("undefined"!=typeof global?global:this);
  </script>
  <script id="loadcss"></script>
</head>

<body>
  <header class="l_header auto shadow blur " style='opacity: 0' >
  <div class='container'>
  <div class='wrapper'>
    <div class='nav-sub'>
      <p class="title"></p>
      <ul class='switcher nav-list-h m-phone' id="pjax-header-nav-list">
        <li><a class="s-comment fas fa-comments fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
          <li><a class="s-toc fas fa-list fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
      </ul>
    </div>
		<div class="nav-main">
      
        
        <a class="title flat-box" target="_self" href='/volantis/'>
          
            <img no-lazy class='logo' src='https://cdn.jsdelivr.net/gh/ccknbc-backup/cdn/logo/logo.png'/>
          
          
          
        </a>
      

			<div class='menu navigation'>
				<ul class='nav-list-h m-pc'>
          
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/
                  
                  
                  
                    id="volantis"
                  >
                  <i class='fas fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/categories/
                  
                  
                  
                    id="volantiscategories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/tags/
                  
                  
                  
                    id="volantistags"
                  >
                  <i class='fas fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/archives/
                  
                  
                  
                    id="volantisarchives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/friends/
                  
                  
                  
                    id="volantisfriends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/about/
                  
                  
                  
                    id="volantisabout"
                  >
                  <i class='fas fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
          
				</ul>
			</div>

      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <i class="icon fas fa-search fa-fw"></i>
          <input type="text" class="input u-search-input" placeholder="Search..." />
        </form>
      </div>

			<ul class='switcher nav-list-h m-phone'>
				
					<li><a class="s-search fas fa-search fa-fw" target="_self" href='javascript:void(0)'></a></li>
				
				<li>
          <a class="s-menu fas fa-bars fa-fw" target="_self" href='javascript:void(0)'></a>
          <ul class="menu-phone list-v navigation white-box">
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/
                  
                  
                  
                    id="volantis"
                  >
                  <i class='fas fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/categories/
                  
                  
                  
                    id="volantiscategories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/tags/
                  
                  
                  
                    id="volantistags"
                  >
                  <i class='fas fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/archives/
                  
                  
                  
                    id="volantisarchives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/friends/
                  
                  
                  
                    id="volantisfriends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/volantis/about/
                  
                  
                  
                    id="volantisabout"
                  >
                  <i class='fas fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
            
          </ul>
        </li>
			</ul>
		</div>
	</div>
  </div>
</header>

  <div class="l_body">
    <div class="l_cover">
  
    
    
        <div class='cover-wrapper post dock' style="display: none;">
          
            <div class='cover-bg lazyload placeholder' data-bg="https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/033.jpg"></div>
          
          <div class='cover-body'>
  <div class='top'>
    
    
      <p class="title">Volantis</p>
    
    
  </div>
  <div class='bottom'>
    <div class='menu navigation'>
      <div class='list-h'>
        
          
            <a href="/volantis/v4/getting-started/"
              
              
              id="volantisv4getting-started">
              <img src='https://cdn.jsdelivr.net/gh/twitter/twemoji@13.0/assets/svg/1f5c3.svg'><p>文档</p>
            </a>
          
            <a href="/volantis/faqs/"
              
              
              id="volantisfaqs">
              <img src='https://cdn.jsdelivr.net/gh/twitter/twemoji@13.0/assets/svg/1f516.svg'><p>帮助</p>
            </a>
          
            <a href="/volantis/examples/"
              
              
              id="volantisexamples">
              <img src='https://cdn.jsdelivr.net/gh/twitter/twemoji@13.0/assets/svg/1f396.svg'><p>示例</p>
            </a>
          
            <a href="/volantis/contributors/"
              
              
              id="volantiscontributors">
              <img src='https://cdn.jsdelivr.net/gh/twitter/twemoji@13.0/assets/svg/1f389.svg'><p>社区</p>
            </a>
          
            <a href="/volantis/archives/"
              
              
              id="volantisarchives">
              <img src='https://cdn.jsdelivr.net/gh/twitter/twemoji@13.0/assets/svg/1f4f0.svg'><p>博客</p>
            </a>
          
            <a target="_blank" rel="noopener" href="https://github.com/volantis-x/hexo-theme-volantis/"
              
              
              id="https:githubcomvolantis-xhexo-theme-volantis">
              <img src='https://cdn.jsdelivr.net/gh/twitter/twemoji@13.0/assets/svg/1f9ec.svg'><p>源码</p>
            </a>
          
        
      </div>
    </div>
  </div>
</div>

          <div class="scroll-down" style="display: none;"><i class="fa fa-chevron-down scroll-down-effects"></i></div>
        </div>
    
  
  </div>
  
    <div class='safearea'>
      <div class='body-wrapper' id="pjax-container">
        
          <!--此文件用来存放一些不方便取值的变量--> 
<!--思路大概是将值藏到重加载的区域内--> 
 
 
 
<div id="pjax-data" style="display: none"> 
  <div id="pjax-ispage">true</div> 
  <div id="pjax-pageTitle">基于小波变换的数字水印检测</div> 
  <div id="pjax-enable-cover">true</div> 
  <div id="pjax-comment-path"></div> 
  <div id="pjax-comment-placeholder"></div> 
</div> 
 
 
<script> 
  // 处理封面 此时 jquery 还没加载 
  if ("half" == "none") { // 移除封面 
    document.getElementsByClassName('cover-wrapper')[0].style.display = "none"; 
    document.getElementsByClassName('l_header', 'cover-wrapper')[0].classList.add("show"); 
  } else { 
    if ("half" == "half") { // 半屏 
      document.getElementsByClassName('cover-wrapper')[0].setAttribute('id', 'half'); 
      document.getElementsByClassName('scroll-down')[0].style.display = "none"; 
    } else if ("half" == "full") { // 全屏 
      document.getElementsByClassName('cover-wrapper')[0].setAttribute('id', 'full'); 
      document.getElementsByClassName('scroll-down')[0].style.display = ""; 
    } 
    document.getElementsByClassName('cover-wrapper')[0].style.display = ""; 
    document.getElementsByClassName('l_header', 'cover-wrapper')[0].classList.remove("show"); 
  } 
</script> 
 

        
        

<div class='l_main'>
  <article class="article post white-box reveal md shadow article-type-post" id="post" itemscope itemprop="blogPost">
  


  
  <div class="article-meta" id="top">
    
    
    
      <h1 class="title">
        基于小波变换的数字水印检测
      </h1>
      <div class='new-meta-box'>
        
          
            
<div class='new-meta-item author'>
  <a class='author' href="/" rel="nofollow">
    <img no-lazy src="">
    <p>请设置文章作者</p>
  </a>
</div>

          
        
          
            
  <div class='new-meta-item category'>
    <a class='notlink'>
      <i class="fas fa-folder-open fa-fw" aria-hidden="true"></i>
      <a class="category-link" href="/volantis/categories/%E5%AD%A6%E4%B9%A0/">学习</a><span class="sep"></span><a class="category-link" href="/volantis/categories/%E5%AD%A6%E4%B9%A0/matlab/">matlab</a>
    </a>
  </div>


          
        
          
            <div class="new-meta-item date">
  <a class='notlink'>
    <i class="fas fa-calendar-alt fa-fw" aria-hidden="true"></i>
    <p>发布于：2020年6月9日</p>
  </a>
</div>

          
        
          
            
  <div class="new-meta-item browse leancloud">
    <a class='notlink'>
      
      <div id="lc-pv" data-title="基于小波变换的数字水印检测" data-path="/volantis/posts/1/">
        <i class="fas fa-eye fa-fw" aria-hidden="true"></i>
        <span id='number'><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span>
        次浏览
      </div>
    </a>
  </div>


          
        
      </div>
    
  </div>


  
  <p><strong>数字水印技术原理</strong></p>
<p>数字水印技术实际上就是通过对水印载体的分析、水印信息的处理、水印嵌入点的选择、嵌入方式的设计、嵌入调制的控制和提取检测方法等相关技术环节进行合理优化，寻求满足不可感知性、鲁棒性和安全性等约束条件下的准最优化设计问题。在实际应用中，一个完整水印系统的设计通常包括水印的生成、嵌入、检测和提取四个部分。</p>
<p><strong>1  水印生成</strong></p>
<p>通常基于伪随机数发生器或混沌系统来产生水印信号，从水印的鲁棒性和安全性方面来考虑，常常需要对原水印进行预处理来适应水印嵌入贺认。</p>
<p><strong>2</strong> <strong>水印嵌入</strong></p>
<p>在尽量保证水印不可感知性的前提下，嵌入最大强度的水印，可提高水印的稳健性。常用的水印嵌入准则有加法准则、乘法准则和融合准则。</p>
<p><strong>3水印检测</strong></p>
<p>指判断水印载体中是否存在水印的过程。</p>
<p><strong>4水印提取</strong></p>
<p>指水印被比较精确地提取的过程。 水印的提取和检测可以需要原始图像的参与（明检测）， 也可不需要原始图像的参与（盲检测）</p>
<p><strong>数字水印的分类</strong></p>
<ul>
<li>按水印的特性</li>
<li>鲁棒数字水印：要求嵌入的水印能够经受各种常用的编辑处理</li>
<li>脆弱数字水印：需要对信号的改动足够敏感，是人们能够根据脆弱水印的状态判断出数据是否被篡改。</li>
<li>按水印的检测过程</li>
<li>明文水印：检测工程中，需要原始数据。普遍来讲，鲁棒性较之盲水印要好。</li>
<li>盲水印：只需要密钥，不需要原始数据</li>
<li>按水印的内容</li>
<li>有意义水印：水印是商标、音频片段等</li>
<li>无意义水印：序列号，仅检测是否含水印</li>
<li>按水印的隐藏位置</li>
<li>空域</li>
<li>频域</li>
<li>时域</li>
</ul>
<p>数字水印技术基本上具有下面几个方面的特点：</p>
<p>—-<strong>安全性</strong>：数字水印的信息应是安全的，难以篡改或伪造，同时，应当有较低的误检测率，当原内容发生变化时，数字水印应当发生变化，从而可以检测原始数据的变更；当然数字水印同样对重复添加有很强的抵抗性</p>
<p>—-<strong>隐蔽性</strong>：数字水印应是不可知觉的，而且应不影响被保护数据的正常使用；不会降质；</p>
<p>—-<strong>鲁棒性</strong>：是指在经历多种无意或有意的信号处理过程后，数字水印仍能保持部分完整性并能被准确鉴别。可能的信号处理过程包括信道噪声、滤波、数/模与模/数转换、重采样、剪切、位移、尺度变化以及有损压缩编码等。</p>
<p>–<strong>嵌入容量</strong>(embedding capacity)：是指载体在不发生形变的前提下可嵌入的水印信息量。尤其是隐蔽通信领域的特殊性，对水印的容量需求很大。</p>
<p><img src="https://t1.picb.cc/uploads/2020/06/09/tfsmLi.gif" class="lazyload" data-srcset="https://t1.picb.cc/uploads/2020/06/09/tfsmLi.gif" srcset="" alt="tfsmLi.gif" loading="lazy"></p>
<p><strong>图1 数字水印嵌入与提取框图</strong></p>
<p><strong>典型的数字水印算法</strong></p>
<p><strong>数字水印的技术实现</strong></p>
<p><strong>空域-LSB</strong></p>
<p>LSB方法是最简单的嵌入水印的方法。事实上 ，任何一幅 图片都具备一定的容噪性 ，这表现在像素数据的最低有效位(Least Significant Bit，LSB)对 人 眼的视觉影响很 小，秘密信息就隐藏在图像每一个像素的最低位或次低位 ，实现其不 可见性。</p>
<p><strong>频域</strong></p>
<p>将图片的灰度强弱，视为图片的频域。通过某种变换手段（傅里叶变换，离散余弦变换，小波变换等）将图像变换到频域（小波域），在频域对图像添加水印，再通过逆变换，将图像转换为空间域。相对于空域手段，频域手段隐匿性更强，抗攻击性更高。</p>
<p><strong>DCT</strong></p>
<p>DCT以8x8的像素为单位进行，生成的是8x8的DCT系数数据块。DCT变换的最大特点是对于一般的图像都能够将像块的能量集中于少数低频DCT系数上，即生成8x8DCT系数块中，仅左上角的少量低频系数数值较大，其余系数的数值很小，这样就可能只编码和传输少数系数而不严重影响图像质量。</p>
<p><strong>小波变换</strong></p>
<p>“小波”就是小区域、长度有限、均值为0的波形。小波变换就是选择适当的基本小波或母小波ψ(t)，通过对基本小波的平移、伸缩而形成一系列的小波，这簇小波作为基可以构成一系列嵌套的（信号）子空间，然后将欲分析的信号（例如图像）投影到各个大小不同的（信号）子空间之中，以观察相应的特性。这样，就相当于我们用不同的焦距去观察一个物体，可从宏观到微观，从概貌到细节观察得十分详尽。所以小波变换又被称为“数学显微镜”。</p>
<p><strong>空域+频域</strong></p>
<p>该算法通过混沌置乱水印图像，建立水印与载体数据之间的Hash单向映射函数，使用两种不同的嵌入方法，先后嵌入水印到空域和DFT域。水印被多次嵌入，实现了水印的盲提取。</p>
<p><strong>数字水印的应用场景</strong></p>
<ul>
<li>多媒体作品盗版追溯：利用数字水印的不可见性，在不影响作品的情况下，加入版权信息的数字水印，可抗击拷贝，剪切。</li>
<li>证件的防伪认证：如学历证，合同，票据等，携带数字水印后，可通过制定的方式提取水印，验证证件等数字文件是否被涂改；</li>
<li>阿里事件：阿里追查泄密员工的时间本身，说明，水印可以有效的追溯信息的释放源；</li>
<li>隐蔽信息传递：水印可携带加密信息后，藏在多媒体文件中传播，并通过特定的提取方式获取水印。可以作为一种隐蔽信息的通信方式。</li>
</ul>
<p><strong>源程序代码及部分注释</strong> </p>
<p><strong>基础设置模块，同时存储着密钥，全局变量 main.m</strong></p>
<figure class="highlight matlab"><table><tr><td class="code"><pre><span class="line"><span class="keyword">global</span> I ;</span><br><span class="line"><span class="keyword">global</span> W;</span><br><span class="line"><span class="keyword">global</span> P;</span><br><span class="line"><span class="keyword">global</span> Iw;</span><br><span class="line">ntimes=<span class="number">23</span>; <span class="comment">% 秘钥1,Arnold置乱次数</span></span><br><span class="line">rngseed=<span class="number">59433</span>; <span class="comment">% 秘钥2，随机数种子</span></span><br><span class="line">flag=<span class="number">1</span>; <span class="comment">%是否显示图像，0 不显示，1 显示</span></span><br></pre></td></tr></table></figure>

<p> <strong>嵌入水印模块：setdwtwatermark.m</strong></p>
<figure class="highlight matlab"><table><tr><td class="code"><pre><span class="line">matlabfunction [Iw,psnr]=setdwtwatermark(I,W,ntimes,rngseed,flag)</span><br><span class="line"><span class="comment">%基于小波变换数字水印嵌入</span></span><br><span class="line"><span class="comment">%I：载体图像，灰度图</span></span><br><span class="line"><span class="comment">%W：水印图像，二值图，且长宽相等</span></span><br><span class="line"><span class="comment">%ntimes: 秘钥1,Arnold置乱次数</span></span><br><span class="line">I=imcrop(I,[<span class="number">0</span> <span class="number">0</span> <span class="built_in">size</span>(I,<span class="number">2</span>) <span class="built_in">size</span>(I,<span class="number">1</span>)<span class="number">-1</span>]);</span><br><span class="line"><span class="built_in">type</span>=class(I);</span><br><span class="line">I=double(I);</span><br><span class="line">W=logical(W);</span><br><span class="line">[mI,nI]=<span class="built_in">size</span>(I);</span><br><span class="line">[mW,nW]=<span class="built_in">size</span>(W);</span><br><span class="line"><span class="keyword">if</span> mW~=nW</span><br><span class="line">  error(<span class="string">&#x27;SETDWTWATERMARK:ARNOLD&#x27;</span>,<span class="string">&#x27;ARNOLD置乱要求水印图像长宽必须相等！&#x27;</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">[ca1,ch1,cv1,cd1]=dwt2(I,<span class="string">&#x27;haar&#x27;</span>);</span><br><span class="line">[ca2,ch2,cv2,cd2]=dwt2(ca1,<span class="string">&#x27;haar&#x27;</span>);</span><br><span class="line"><span class="keyword">if</span> flag</span><br><span class="line">  <span class="built_in">figure</span>(<span class="string">&#x27;Name&#x27;</span>,<span class="string">&#x27;载体小波分解&#x27;</span>)</span><br><span class="line">  subplot(<span class="number">121</span>)</span><br><span class="line">  imagesc([wcodemat(ca1),wcodemat(ch1);wcodemat(cv1),wcodemat(cd1)])</span><br><span class="line">  title(<span class="string">&#x27;一级小波分解&#x27;</span>)</span><br><span class="line">  subplot(<span class="number">122</span>)</span><br><span class="line">  imagesc([wcodemat(ca2),wcodemat(ch2);wcodemat(cv2),wcodemat(cd2)])</span><br><span class="line">  title(<span class="string">&#x27;二级小波分解&#x27;</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">Wa=W;</span><br><span class="line">H=[<span class="number">1</span>,<span class="number">1</span>;<span class="number">1</span>,<span class="number">2</span>]^ntimes; </span><br><span class="line"><span class="keyword">for</span> <span class="built_in">i</span>=<span class="number">1</span>:nW</span><br><span class="line">  <span class="keyword">for</span> <span class="built_in">j</span>=<span class="number">1</span>:nW</span><br><span class="line">​    idx=<span class="built_in">mod</span>(H*[<span class="built_in">i</span><span class="number">-1</span>;<span class="built_in">j</span><span class="number">-1</span>],nW)+<span class="number">1</span>;</span><br><span class="line">​    Wa(idx(<span class="number">1</span>),idx(<span class="number">2</span>))=W(<span class="built_in">i</span>,<span class="built_in">j</span>);</span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span> </span><br><span class="line">flag=<span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span> flag</span><br><span class="line">  <span class="built_in">figure</span>(<span class="string">&#x27;Name&#x27;</span>,<span class="string">&#x27;水印置乱与嵌入&#x27;</span>)</span><br><span class="line">  subplot(<span class="number">221</span>)</span><br><span class="line">  imshow(W)</span><br><span class="line">  title(<span class="string">&#x27;原始水印&#x27;</span>)</span><br><span class="line">  subplot(<span class="number">222</span>)</span><br><span class="line">  imshow(Wa)</span><br><span class="line">  title([<span class="string">&#x27;置乱水印，变换次数=&#x27;</span>,num2str(ntimes)]);</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="comment">%数字水印嵌入</span></span><br><span class="line">ca2w=ca2;</span><br><span class="line">rng(rngseed);</span><br><span class="line">idx=randperm(<span class="built_in">numel</span>(ca2),<span class="built_in">numel</span>(Wa));</span><br><span class="line"><span class="keyword">for</span> <span class="built_in">i</span>=<span class="number">1</span>:<span class="built_in">numel</span>(Wa)</span><br><span class="line">  c=ca2(idx(<span class="built_in">i</span>));</span><br><span class="line">  z=<span class="built_in">mod</span>(c,nW);</span><br><span class="line">  <span class="keyword">if</span> Wa(<span class="built_in">i</span>)</span><br><span class="line">​    <span class="keyword">if</span> z&lt;nW/<span class="number">4</span></span><br><span class="line">​      f=c-nW/<span class="number">4</span>-z;</span><br><span class="line">​    <span class="keyword">else</span></span><br><span class="line">​      f=c+nW*<span class="number">3</span>/<span class="number">4</span>-z;</span><br><span class="line">​    <span class="keyword">end</span></span><br><span class="line">  <span class="keyword">else</span> </span><br><span class="line">​    <span class="keyword">if</span> z&lt;nW*<span class="number">3</span>/<span class="number">4</span></span><br><span class="line">​      f=c+nW/<span class="number">4</span>-z;</span><br><span class="line">​    <span class="keyword">else</span></span><br><span class="line">​      f=c+nW*<span class="number">5</span>/<span class="number">4</span>-z;</span><br><span class="line">​    <span class="keyword">end</span></span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line">  ca2w(idx(<span class="built_in">i</span>))=f;</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">ca1w=idwt2(ca2w,ch2,cv2,cd2,<span class="string">&#x27;haar&#x27;</span>);</span><br><span class="line">Iw=idwt2(ca1w,ch1,cv1,cd1,<span class="string">&#x27;haar&#x27;</span>);</span><br><span class="line">Iw=Iw(<span class="number">1</span>:mI,<span class="number">1</span>:nI);</span><br><span class="line">mn=<span class="built_in">numel</span>(I);</span><br><span class="line">Imax=<span class="built_in">max</span>(I(:));</span><br><span class="line">psnr=<span class="number">10</span>*<span class="built_in">log10</span>(mn*Imax^<span class="number">2</span>/sum((I(:)-Iw(:)).^<span class="number">2</span>));</span><br><span class="line">I=cast(I,<span class="built_in">type</span>);</span><br><span class="line">Iw=cast(Iw,<span class="built_in">type</span>);</span><br><span class="line"><span class="keyword">if</span> flag</span><br><span class="line">  subplot(<span class="number">223</span>)</span><br><span class="line">  imshow(I);</span><br><span class="line">  title(<span class="string">&#x27;原始图像&#x27;</span>)</span><br><span class="line">  subplot(<span class="number">224</span>);</span><br><span class="line">  imshow(Iw);</span><br><span class="line">  title([<span class="string">&#x27;添加水印，PSNR=&#x27;</span>,num2str(psnr)]);</span><br><span class="line"><span class="keyword">end</span> </span><br></pre></td></tr></table></figure>

<p><strong>提取水印模块：getdwtwatermark.m</strong></p>
<figure class="highlight matlab"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="params">[Wg,nc]</span>=<span class="title">getdwtwatermark</span><span class="params">(Iw,W,P,ntimes,rngseed,flag)</span></span></span><br><span class="line">[mW,nW]=<span class="built_in">size</span>(W);</span><br><span class="line"><span class="keyword">if</span> mW~=nW</span><br><span class="line">  error(<span class="string">&#x27;GETDWTWATERMARK:ARNOLD&#x27;</span>,<span class="string">&#x27;ARNOLD置乱要求水印图像长宽必须相等！&#x27;</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">Iw=double(Iw);</span><br><span class="line">W=logical(W);</span><br><span class="line">ca1w=dwt2(Iw,<span class="string">&#x27;haar&#x27;</span>);</span><br><span class="line">ca2w=dwt2(ca1w,<span class="string">&#x27;haar&#x27;</span>);</span><br><span class="line">Wa=W;</span><br><span class="line">rng(rngseed);</span><br><span class="line">idx=randperm(<span class="built_in">numel</span>(ca2w),<span class="built_in">numel</span>(Wa));</span><br><span class="line"><span class="keyword">for</span> <span class="built_in">i</span>=<span class="number">1</span>:<span class="built_in">numel</span>(Wa)</span><br><span class="line">  c=ca2w(idx(<span class="built_in">i</span>));</span><br><span class="line">  z=<span class="built_in">mod</span>(c,nW);</span><br><span class="line">  <span class="keyword">if</span> z&lt;nW/<span class="number">2</span></span><br><span class="line">​    Wa(<span class="built_in">i</span>)=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">​    Wa(<span class="built_in">i</span>)=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">Wg=Wa;</span><br><span class="line">H=[<span class="number">2</span> <span class="number">-1</span>;<span class="number">-1</span>,<span class="number">1</span>]^ntimes;</span><br><span class="line"><span class="keyword">for</span> <span class="built_in">i</span>=<span class="number">1</span>:nW</span><br><span class="line">  <span class="keyword">for</span> <span class="built_in">j</span>=<span class="number">1</span>:nW</span><br><span class="line">​    idx=<span class="built_in">mod</span>(H*[<span class="built_in">i</span><span class="number">-1</span>;<span class="built_in">j</span><span class="number">-1</span>],nW)+<span class="number">1</span>;</span><br><span class="line">​    Wg(idx(<span class="number">1</span>),idx(<span class="number">2</span>))=Wa(<span class="built_in">i</span>,<span class="built_in">j</span>);</span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">nc=sum(Wg(:).*W(:))/<span class="built_in">sqrt</span>(sum(Wg(:).^<span class="number">2</span>))/<span class="built_in">sqrt</span>(sum(W(:).^<span class="number">2</span>));</span><br><span class="line">flag=<span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span> flag</span><br><span class="line">  <span class="built_in">figure</span>(<span class="string">&#x27;Name&#x27;</span>,<span class="string">&#x27;数字水印提取结果&#x27;</span>)</span><br><span class="line">  subplot(<span class="number">121</span>)</span><br><span class="line">  W=imcrop(W,[<span class="number">0</span> <span class="number">0</span> <span class="built_in">size</span>(P,<span class="number">2</span>) <span class="built_in">size</span>(P,<span class="number">1</span>)]);  </span><br><span class="line"> imshow(W)</span><br><span class="line">  title(<span class="string">&#x27;原始水印&#x27;</span>)</span><br><span class="line">  subplot(<span class="number">122</span>)</span><br><span class="line">  Wg=imcrop(Wg,[<span class="number">0</span> <span class="number">0</span> <span class="number">122</span> <span class="number">107</span>]);</span><br><span class="line">  imshow(Wg)</span><br><span class="line">  title([<span class="string">&#x27;提取水印，NC=&#x27;</span>,num2str(nc)]);</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>

<p><strong>GUI调用模块 watermatk.m</strong></p>
<figure class="highlight matlab"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">varargout</span> = <span class="title">watermark</span><span class="params">(varargin)</span></span></span><br><span class="line">gui_Singleton = <span class="number">1</span>;</span><br><span class="line">gui_State = struct(<span class="string">&#x27;gui_Name&#x27;</span>,    mfilename, ...</span><br><span class="line">​          <span class="string">&#x27;gui_Singleton&#x27;</span>, gui_Singleton, ...</span><br><span class="line">​          <span class="string">&#x27;gui_OpeningFcn&#x27;</span>, @watermark_OpeningFcn, ...</span><br><span class="line">​          <span class="string">&#x27;gui_OutputFcn&#x27;</span>, @watermark_OutputFcn, ...</span><br><span class="line">​          <span class="string">&#x27;gui_LayoutFcn&#x27;</span>, [] , ...</span><br><span class="line">​          <span class="string">&#x27;gui_Callback&#x27;</span>,  []);</span><br><span class="line"><span class="keyword">if</span> nargin &amp;&amp; ischar(varargin&#123;<span class="number">1</span>&#125;)</span><br><span class="line">  gui_State.gui_Callback = str2func(varargin&#123;<span class="number">1</span>&#125;);</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="keyword">if</span> nargout</span><br><span class="line">  [varargout&#123;<span class="number">1</span>:nargout&#125;] = gui_mainfcn(gui_State, varargin&#123;:&#125;);</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  gui_mainfcn(gui_State, varargin&#123;:&#125;);</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">watermark_OpeningFcn</span><span class="params">(hObject, eventdata, handles, varargin)</span></span></span><br><span class="line">movegui( hObject,<span class="string">&#x27;center&#x27;</span> );</span><br><span class="line">handles.output = hObject;<span class="comment">%移动窗口到屏幕中心</span></span><br><span class="line">guidata(hObject, handles);</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">varargout</span> = <span class="title">watermark_OutputFcn</span><span class="params">(hObject, eventdata, handles)</span> </span></span><br><span class="line">varargout&#123;<span class="number">1</span>&#125; = handles.output </span><br><span class="line"><span class="comment">% --- Executes on button press in pushbutton1.</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">pushbutton1_Callback</span><span class="params">(hObject, eventdata, handles)</span> </span></span><br><span class="line">main</span><br><span class="line">[filename pathname]=uigetfile(&#123;<span class="string">&#x27;*.jpg;*.bmp&#x27;</span>;<span class="string">&#x27;*.*&#x27;</span>&#125;,<span class="string">&#x27;请选择底图文件&#x27;</span>);</span><br><span class="line"><span class="comment">%%打开图像</span></span><br><span class="line">I=imread(filename); <span class="comment">%读取原始图像</span></span><br><span class="line">[filename pathname]=uigetfile(&#123;<span class="string">&#x27;*.png;*.tif&#x27;</span>;<span class="string">&#x27;*.*&#x27;</span>&#125;,<span class="string">&#x27;请选择水印文件&#x27;</span>);</span><br><span class="line">helpdlg(<span class="string">&#x27;选择完毕，您可点击查看以确认是否需要重新选择&#x27;</span>,<span class="string">&#x27;提示&#x27;</span>);</span><br><span class="line">I = rgb2gray(I);<span class="comment">%转换为灰度图</span></span><br><span class="line">W = imread(filename);<span class="comment">%读取水印图像</span></span><br><span class="line"><span class="keyword">if</span> strcmp(filename(<span class="keyword">end</span><span class="number">-3</span>:<span class="keyword">end</span>),<span class="string">&#x27;tif&#x27;</span>)</span><br><span class="line">  W= rgb2gray(W);</span><br><span class="line">  W=imbinarize(W);<span class="comment">%转化成二值图像</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">P=W;</span><br><span class="line">Mm=<span class="built_in">size</span>(W,<span class="number">1</span>); <span class="comment">%水印的行数</span></span><br><span class="line">Nm=<span class="built_in">size</span>(W,<span class="number">2</span>); <span class="comment">%水印的列数</span></span><br><span class="line"><span class="comment">%将图像扩展为长宽相等</span></span><br><span class="line"><span class="keyword">if</span> Mm&lt;Nm</span><br><span class="line">  Max=Nm;</span><br><span class="line">  Min=Mm;</span><br><span class="line">  <span class="keyword">for</span> <span class="built_in">i</span>=Min:Max</span><br><span class="line">​    <span class="keyword">for</span> <span class="built_in">j</span>=<span class="number">1</span>:Max</span><br><span class="line">​      W(<span class="built_in">i</span>,<span class="built_in">j</span>)=<span class="number">1</span>;</span><br><span class="line">​    <span class="keyword">end</span></span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">else</span> </span><br><span class="line">  Max=Mm;</span><br><span class="line">  Min=Nm;</span><br><span class="line">  <span class="keyword">for</span> <span class="built_in">i</span>=<span class="number">1</span>:Max</span><br><span class="line">​    <span class="keyword">for</span> <span class="built_in">j</span>=Min:Max</span><br><span class="line">​      W(<span class="built_in">i</span>,<span class="built_in">j</span>)=<span class="number">1</span>;</span><br><span class="line">​    <span class="keyword">end</span></span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span>  </span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">pushbutton2_Callback</span><span class="params">(hObject, eventdata, handles)</span></span></span><br><span class="line">main</span><br><span class="line"><span class="built_in">figure</span>(<span class="string">&#x27;Name&#x27;</span>,<span class="string">&#x27;载体图像与水印图像&#x27;</span>)</span><br><span class="line">subplot(<span class="number">211</span>);</span><br><span class="line">imshow(I);</span><br><span class="line">title(<span class="string">&#x27;载体图像&#x27;</span>)</span><br><span class="line">subplot(<span class="number">212</span>);</span><br><span class="line">imshow(P);</span><br><span class="line">title(<span class="string">&#x27;水印图像&#x27;</span>) </span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">pushbutton3_Callback</span><span class="params">(hObject, eventdata, handles)</span></span></span><br><span class="line">main</span><br><span class="line">[Iw,psnr]=setdwtwatermark(I,W,ntimes,rngseed,<span class="number">0</span>); </span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">pushbutton4_Callback</span><span class="params">(hObject, eventdata, handles)</span></span></span><br><span class="line">main</span><br><span class="line">[Wg,nc]=getdwtwatermark(Iw,W,P,ntimes,rngseed,<span class="number">0</span>);</span><br></pre></td></tr></table></figure>

<p><strong>基于小波的水印技术 运行结果演示</strong></p>
<p><strong>1图像的选择与导入</strong></p>
<p>进入GUI主界面</p>
<p><img src="https://t1.picb.cc/uploads/2020/06/09/tfsGAW.png" class="lazyload" data-srcset="https://t1.picb.cc/uploads/2020/06/09/tfsGAW.png" srcset="" alt="tfsGAW.png" loading="lazy"></p>
<p><strong>图2 GUI主界面</strong></p>
<p>点击选择图片，分别选择载体图像和水印图像</p>
<p><img src="https://t1.picb.cc/uploads/2020/06/09/tfs4wL.png" class="lazyload" data-srcset="https://t1.picb.cc/uploads/2020/06/09/tfs4wL.png" srcset="" alt="tfs4wL.png" loading="lazy"></p>
<p><strong>图3 选择底图文件</strong></p>
<p><img src="https://t1.picb.cc/uploads/2020/06/09/tfsHFw.png" class="lazyload" data-srcset="https://t1.picb.cc/uploads/2020/06/09/tfsHFw.png" srcset="" alt="tfsHFw.png" loading="lazy"></p>
<p><strong>图4 选择水印文件</strong></p>
<p>两项都选择完毕后提示选择完成，</p>
<p><img src="https://t1.picb.cc/uploads/2020/06/09/tfschv.png" class="lazyload" data-srcset="https://t1.picb.cc/uploads/2020/06/09/tfschv.png" srcset="" alt="tfschv.png" loading="lazy"></p>
<p><strong>图5 选择完毕 帮助提示框</strong></p>
<p>对于取消选择未设置警告与报错提示，防止过多弹窗</p>
<p><strong>2载体图像小波变换</strong></p>
<p>这部分未单独新开按钮和图片显示窗口，但程序的确执行了，用户无需知晓</p>
<p><strong>3水印图像的预处理</strong></p>
<p><img src="https://t1.picb.cc/uploads/2020/06/09/tfs5JR.png" class="lazyload" data-srcset="https://t1.picb.cc/uploads/2020/06/09/tfs5JR.png" srcset="" alt="tfs5JR.png" loading="lazy"></p>
<p><strong>图6 预处理成二值图像后的水印</strong></p>
<p><strong>4小波数字水印的嵌入</strong></p>
<p><img src="https://t1.picb.cc/uploads/2020/06/09/tfsAjg.png" class="lazyload" data-srcset="https://t1.picb.cc/uploads/2020/06/09/tfsAjg.png" srcset="" alt="tfsAjg.png" loading="lazy"></p>
<p><strong>图7 原始水印和置乱水印以及原始图像和添加水印之后的图像对比</strong></p>
<p><strong>5小波数字水印的提取</strong></p>
<p><img src="https://t1.picb.cc/uploads/2020/06/09/tfs848.png" class="lazyload" data-srcset="https://t1.picb.cc/uploads/2020/06/09/tfs848.png" srcset="" alt="tfs848.png" loading="lazy"></p>
<p><strong>图8 原始水印和提取的水印的对比</strong></p>
<p>将原始水印和提取水印进行对比，两个相关系数为0.99356，相关系数越接近1, 则提取的水印和原始水印越相似。</p>

  
  
    
    <div class='footer'>
      
      
      
        <div class='copyright'>
          <blockquote>
            
              
                <p>博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</p>

              
            
              
                <p>本文永久链接是：<a href=https://ccknbc.github.io/volantis/posts/1/>https://ccknbc.github.io/volantis/posts/1/</a></p>
              
            
          </blockquote>
        </div>
      
      
    </div>
  
  
    


  <div class='article-meta' id="bottom">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateUpdated" datetime="2020-06-09T13:00:00+00:00">
  <a class='notlink'>
    <i class="fas fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：2020年6月9日</p>
  </a>
</div>

        
      
        
          
  
  <div class="new-meta-item meta-tags"><a class="tag" href="/volantis/tags/study/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>study</p></a></div> <div class="new-meta-item meta-tags"><a class="tag" href="/volantis/tags/watermark/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>watermark</p></a></div> <div class="new-meta-item meta-tags"><a class="tag" href="/volantis/tags/matlab/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>matlab</p></a></div>


        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=https://ccknbc.github.io/volantis/posts/1/&title=基于小波变换的数字水印检测 - CC康纳百川&summary="
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qq.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qq.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=https://ccknbc.github.io/volantis/posts/1/&title=基于小波变换的数字水印检测 - CC康纳百川&summary="
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qzone.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qzone.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://service.weibo.com/share/share.php?url=https://ccknbc.github.io/volantis/posts/1/&title=基于小波变换的数字水印检测 - CC康纳百川&summary="
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/weibo.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/weibo.png" srcset="">
          
        </a>
      
    
      
    
      
    
  </div>
</div>



        
      
    </div>
  </div>


  
  

  
    <div class="prev-next">
      
        <a class='prev' href='/volantis/posts/2/'>
          <p class='title'><i class="fas fa-chevron-left" aria-hidden="true"></i>生产实习</p>
          <p class='content'>等待更新中 时间为整点定时发布

待同步 取消加密
图片还未上传至图床
视频嵌入还未做好 YouTube已OK 
目录在左侧

超声波及温度传感摇摆风扇所用元件：UNO R3开发板 ×1
大面包...</p>
        </a>
      
      
    </div>
  
</article>


  

  






</div>
<aside class='l_side'>
  
  
    
    




  


</aside>



      </div>
      
  
  <footer class="footer clearfix">
    <br><br>
    
      
        <div class="aplayer-container">
          


        </div>
      
    
      
        <br>
        <div class="social-wrapper">
          
            
          
            
          
            
          
        </div>
      
    
      
        <div><p>博客内容遵循 <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</a></p>
</div>
      
    
      
        
          <div><p><span id="lc-sv">本站总访问量为 <span id='number'><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span> 次</span> <span id="lc-uv">访客数为 <span id='number'><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span> 人</span></p>
</div>
        
      
    
      
        本站使用
        <a href="https://github.com/volantis-x/hexo-theme-volantis/tree/4.0.0-rc.4" target="_blank" class="codename">Volantis</a>
        作为主题
      
    
      
        <div class='copyright'>
        <p><a href="/">Copyright © 2017-2020 XXX</a></p>

        </div>
      
    
  </footer>


      <a class="s-top fas fa-arrow-up fa-fw" href='javascript:void(0)'></a>
    </div>
  </div>
  <div>
    <!-- required -->

<script src="https://cdn.jsdelivr.net/npm/jquery@3.5/dist/jquery.min.js"></script>

<script>
  function pjax_fancybox() {
    $(".md .gallery").find("img").each(function () { //渲染 fancybox
      var element = document.createElement("a"); // a 标签
      $(element).attr("class", "fancybox");
      $(element).attr("pjax-fancybox", "");  // 过滤 pjax
      $(element).attr("href", $(this).attr("src"));
      if ($(this).attr("data-original")) {
        $(element).attr("href", $(this).attr("data-original"));
      }
      $(element).attr("data-fancybox", "images");
      var caption = "";   // 描述信息
      if ($(this).attr('alt')) {  // 判断当前页面是否存在描述信息
        $(element).attr('data-caption', $(this).attr('alt'));
        caption = $(this).attr('alt');
      }
      var div = document.createElement("div");
      $(div).addClass("fancybox");
      $(this).wrap(div); // 最外层套 div ，其实主要作用还是 class 样式
      var span = document.createElement("span");
      $(span).addClass("image-caption");
      $(span).text(caption); // 加描述
      $(this).after(span);  // 再套一层描述
      $(this).wrap(element);  // 最后套 a 标签
    })
    $(".md .gallery").find("img").fancybox({
      selector: '[data-fancybox="images"]',
      hash: false,
      loop: false,
      closeClick: true,
      helpers: {
        overlay: {closeClick: true}
      },
      buttons: [
        "zoom",
        "close"
      ]
    });
  };
  function SCload_fancybox() {
    if ($(".md .gallery").find("img").length == 0) return;
    loadCSS("https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css", document.getElementById("loadcss"));
    setTimeout(function() {
      loadScript('https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js', pjax_fancybox)
    }, 1);
  };
  $(function () {
    SCload_fancybox();
  });
</script>


<!-- internal -->







  <script defer src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@17.1.0/dist/lazyload.min.js"></script>
<script>
  // https://www.npmjs.com/package/vanilla-lazyload
  // Set the options globally
  // to make LazyLoad self-initialize
  window.lazyLoadOptions = {
    elements_selector: ".lazyload",
    threshold: 0
  };
  // Listen to the initialization event
  // and get the instance of LazyLoad
  window.addEventListener(
    "LazyLoad::Initialized",
    function (event) {
      window.lazyLoadInstance = event.detail.instance;
    },
    false
  );
  document.addEventListener('DOMContentLoaded', function () {
    lazyLoadInstance.update();
  });
  document.addEventListener('pjax:complete', function () {
    lazyLoadInstance.update();
  });
</script>




  
  
    <script>
      window.FPConfig = {
        delay: 0,
        ignoreKeywords: [],
        maxRPS: 5,
        hoverDelay: 25
      };
    </script>
    <script defer src="https://cdn.jsdelivr.net/gh/gijo-varghese/flying-pages@2.1.2/flying-pages.min.js"></script>
  















  
<script src="/volantis/js/app.js"></script>




  
    
<script src="/volantis/js/search.js"></script>

  


<!-- optional -->

  <script>
const SearchServiceimagePath="https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@master/img/";
const ROOT =  ("/volantis/" || "/").endsWith('/') ? ("/volantis/" || "/") : ("/volantis//" || "/" );
(function ($) {
  
    customSearch = new HexoSearch({
      imagePath: SearchServiceimagePath
    });
  
})(jQuery);

</script>











  <script defer>

  const LCCounter = {
    app_id: 'yPKOAo6M4zHWCq3jzS0iqLEW-MdYXbMMI',
    app_key: 'Vt6syMPvpjnscIVTQNFAX0jj',
    custom_api_server: '',

    // 查询存储的记录
    getRecord(Counter, url, title) {
      return new Promise(function (resolve, reject) {
        Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify({url})))
          .then(resp => resp.json())
          .then(({results, code, error}) => {
            if (code === 401) {
              throw error;
            }
            if (results && results.length > 0) {
              var record = results[0];
              resolve(record);
            } else {
              Counter('post', '/classes/Counter', {url, title: title, times: 0})
                .then(resp => resp.json())
                .then((record, error) => {
                  if (error) {
                    throw error;
                  }
                  resolve(record);
                }).catch(error => {
                console.error('Failed to create', error);
                reject(error);
              });
            }
          }).catch((error) => {
          console.error('LeanCloud Counter Error:', error);
          reject(error);
        });
      })
    },

    // 发起自增请求
    increment(Counter, incrArr) {
      return new Promise(function (resolve, reject) {
        Counter('post', '/batch', {
          "requests": incrArr
        }).then((res) => {
          res = res.json();
          if (res.error) {
            throw res.error;
          }
          resolve(res);
        }).catch((error) => {
          console.error('Failed to save visitor count', error);
          reject(error);
        });
      });
    },

    // 构建自增请求体
    buildIncrement(objectId) {
      return {
        "method": "PUT",
        "path": `/1.1/classes/Counter/${ objectId }`,
        "body": {
          "times": {
            '__op': 'Increment',
            'amount': 1
          }
        }
      }
    },

    // 校验是否为有效的 UV
    validUV() {
      var key = 'LeanCloudUVTimestamp';
      var flag = localStorage.getItem(key);
      if (flag) {
        // 距离标记小于 24 小时则不计为 UV
        if (new Date().getTime() - parseInt(flag) <= 86400000) {
          return false;
        }
      }
      localStorage.setItem(key, new Date().getTime().toString());
      return true;
    },

    addCount(Counter) {
      var enableIncr = '' === 'true' && window.location.hostname !== 'localhost';
      enableIncr = true;
      var getterArr = [];
      var incrArr = [];
      // 请求 PV 并自增
      var pvCtn = document.querySelector('#lc-sv');
      if (pvCtn || enableIncr) {
        var pvGetter = this.getRecord(Counter, 'https://ccknbc.github.io/volantis' + '/#lc-sv', 'Visits').then((record) => {
          incrArr.push(this.buildIncrement(record.objectId))
          var eles = document.querySelectorAll('#lc-sv #number');
          if (eles.length > 0) {
            eles.forEach((el,index,array)=>{
              el.innerText = record.times + 1;
              if (pvCtn) {
                pvCtn.style.display = 'inline';
              }
            })
          }
        });
        getterArr.push(pvGetter);
      }

      // 请求 UV 并自增
      var uvCtn = document.querySelector('#lc-uv');
      if (uvCtn || enableIncr) {
        var uvGetter = this.getRecord(Counter, 'https://ccknbc.github.io/volantis' + '/#lc-uv', 'Visitors').then((record) => {
          var vuv = this.validUV();
          vuv && incrArr.push(this.buildIncrement(record.objectId))
          var eles = document.querySelectorAll('#lc-uv #number');
          if (eles.length > 0) {
            eles.forEach((el,index,array)=>{
              el.innerText = record.times + (vuv ? 1 : 0);
              if (uvCtn) {
                uvCtn.style.display = 'inline';
              }
            })
          }
        });
        getterArr.push(uvGetter);
      }

      // 请求文章的浏览数，如果是当前页面就自增
      var allPV = document.querySelectorAll('#lc-pv');
      if (allPV.length > 0 || enableIncr) {
        for (i = 0; i < allPV.length; i++) {
          let pv = allPV[i];
          let title = pv.getAttribute('data-title');
          var url = 'https://ccknbc.github.io/volantis' + pv.getAttribute('data-path');
          if (url) {
            var viewGetter = this.getRecord(Counter, url, title).then((record) => {
              // 是当前页面就自增
              let curPath = window.location.pathname;
              if (curPath.includes('index.html')) {
                curPath = curPath.substring(0, curPath.lastIndexOf('index.html'));
              }
              if (pv.getAttribute('data-path') == curPath) {
                incrArr.push(this.buildIncrement(record.objectId));
              }
              if (pv) {
                var ele = pv.querySelector('#lc-pv #number');
                if (ele) {
                  if (pv.getAttribute('data-path') == curPath) {
                    ele.innerText = (record.times || 0) + 1;
                  } else {
                    ele.innerText = record.times || 0;
                  }
                  pv.style.display = 'inline';
                }
              }
            });
            getterArr.push(viewGetter);
          }
        }
      }

      // 如果启动计数自增，批量发起自增请求
      if (enableIncr) {
        Promise.all(getterArr).then(() => {
          incrArr.length > 0 && this.increment(Counter, incrArr);
        })
      }

    },


    fetchData(api_server) {
      var Counter = (method, url, data) => {
        return fetch(`${ api_server }/1.1${ url }`, {
          method,
          headers: {
            'X-LC-Id': this.app_id,
            'X-LC-Key': this.app_key,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data)
        });
      };
      this.addCount(Counter);
    },

    refreshCounter() {
      var api_server = this.app_id.slice(-9) !== '-MdYXbMMI' ? this.custom_api_server : `https://${ this.app_id.slice(0, 8).toLowerCase() }.api.lncldglobal.com`;
      if (api_server) {
        this.fetchData(api_server);
      } else {
        fetch('https://app-router.leancloud.cn/2/route?appId=' + this.app_id)
          .then(resp => resp.json())
          .then(({api_server}) => {
            this.fetchData('https://' + api_server);
          });
      }
    }

  };

  LCCounter.refreshCounter();

  document.addEventListener('pjax:complete', function () {
    LCCounter.refreshCounter();
  });
</script>



<!-- more -->


    
      


<script src="https://cdn.jsdelivr.net/npm/pjax@0.2.8/pjax.min.js"></script>

<!-- 样式位于：source/css/_third-party/pjaxanimate.styl -->

<div class="pjax-animate">
  
    <script src="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js"></script>
    <div id="loading-bar-wrapper"><script>NProgress.configure({parent:"#loading-bar-wrapper",trickleSpeed: 100})</script></div>
    <script>
      window.ShowLoading = function() {
        NProgress.start();
      };
      window.HideLoading = function() {
        NProgress.done();
      }
    </script>
  
</div>

<script>
    var pjax;
    document.addEventListener('DOMContentLoaded', function () {
      pjax = new Pjax({
        elements: 'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([pjax-fancybox])',
        selectors: [
          "title",
          "#pjax-container",
          "#pjax-header-nav-list"
        ],
        cacheBust: false,   // url 地址追加时间戳，用以避免浏览器缓存
        timeout: 5000
      });
    });

    document.addEventListener('pjax:send', function (e) {
      window.stop(); // 相当于点击了浏览器的停止按钮

      try {
        var currentUrl = window.location.pathname;
        var targetUrl = e.triggerElement.href;
        var banUrl = [""];
        if (banUrl[0] != "") {
          banUrl.forEach(item => {
            if(currentUrl.indexOf(item) != -1 || targetUrl.indexOf(item) != -1) {
              window.location.href = targetUrl;
            }
          });
        }
      } catch (error) {}

      window.subData = null; // 移除标题（用于一二级导航栏切换处）
      if (typeof $.fancybox != "undefined") {
        $.fancybox.close();    // 关闭弹窗
      }
      $('.l_header .switcher .s-search').removeClass('active'); // 关闭移动端激活的搜索框
      $('.l_header').removeClass('z_search-open'); // 关闭移动端激活的搜索框
      $('.wrapper').removeClass('sub'); // 跳转页面时关闭二级导航

      // 解绑事件 避免重复监听
      $('.s-top').unbind('click');
      $('.menu a').unbind('click');
      $(window).unbind('resize');
      $(window).unbind('scroll');
      $(document).unbind('scroll');
      $(document).unbind('click');
      $('body').unbind('click');
      window.ShowLoading();
    });

    document.addEventListener('pjax:complete', function () {
      // 关于百度统计对 SPA 页面的处理：
      // 方案一：百度统计>管理>单页应用设置中，打开开启按钮即可对SPA进行统计。 https://tongji.baidu.com/web/help/article?id=324
      // 方案二：取消注释下列代码。 https://tongji.baidu.com/web/help/article?id=235
      // 

      // 关于谷歌统计对 SPA 页面的处理：
      // 当应用以动态方式加载内容并更新地址栏中的网址时，也应该更新通过 gtag.js 存储的网页网址。
      // https://developers.google.cn/analytics/devguides/collection/gtagjs/single-page-applications?hl=zh-cn
      

      $('.nav-main').find('.list-v').not('.menu-phone').removeAttr("style",""); // 移除小尾巴的移除
      $('.menu-phone.list-v').removeAttr("style",""); // 移除小尾巴的移除
      $('script[data-pjax], .pjax-reload script').each(function () {
        $(this).parent().append($(this).remove());
      });
      try{
          if (typeof $.fancybox == "undefined") {
            SCload_fancybox();
          } else {
            pjax_fancybox();
          }
        
        
        
        
        
        
        
        
        
        
      } catch (e) {
        console.log(e);
      }
      window.HideLoading();
    });

    document.addEventListener('pjax:error', function (e) {
      window.HideLoading();
      window.location.href = e.triggerElement.href;
    });
</script>

    
  </div>
</body>
</html>
